package it.polimi.loganalyzer.model;

import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;

public class Model {

	private Set<State> states;
	private Set<Transition> transitions;
	private State initState;
	private State goalState;
	private Logger log = Logger.getLogger(this.getClass().getSimpleName());

	public Model(String init, String goal, int initReward, int goalReward) {
		this.states = new HashSet<State>();
		this.transitions = new HashSet<Transition>();
		this.initState = new State(init, initReward);
		this.goalState = new State(goal, goalReward);
		this.states.add(initState);
		this.states.add(goalState);
	}

	public State getInitialState() {
		return this.initState;
	}

	public State getFinalState() {
		return this.goalState;
	}

	public boolean containsState(State state) {
		return states.contains(state);
	}

	public boolean containsTransition(Transition transition) {
		return transitions.contains(transition);
	}

	public int numberOfStates() {
		return states.size();
	}

	public int numberOfTransitions() {
		return transitions.size();
	}

	public boolean insertState(State state) {
		return this.states.add(state);
	}

	public int signalTransition(State source, State destination) {
		if (!states.contains(source))
			throw new IllegalArgumentException("State " + source + " does not exist.");
		if (!states.contains(destination))
			throw new IllegalArgumentException("State " + destination + " does not exist.");
		Transition t = this.createTransition(source, destination);
		return t.getOccurrences();
	}

	private Transition createTransition(State source, State destination) {
		Transition transition = new Transition(source, destination);
		for (Transition t : transitions) {
			if (t.equals(transition)) {
				transition = t;
			}
		}
		transition.incrementOccurrences();
		transitions.add(transition);
		return transition;
	}

	private void finalizeModel() {
		this.createTransition(goalState, goalState);
		this.attachingOrphans();
		this.attachingChildless();
	}

	private void attachingChildless() {
		for (State source : states) {
			boolean childLess = true;
			for (State destination : states) {
				// Goal and Initial States are not childless
				if (destination.equals(this.goalState)
						| destination.equals(this.initState)) {
					continue;
				}
				Transition t = new Transition(source, destination);
				if (transitions.contains(t)) {
					childLess = false;
				}
			}
			// If current state is childless we add a transition to the final
			// state
			if (childLess) {
				createTransition(source, this.goalState);
				log.debug("State " + source.getName()
						+ " is childless, fixing it.");
			}
		}
	}

	private void attachingOrphans() {
		for (State destination : states) {
			boolean orphan = true;
			for (State source : states) {
				// Goal and Initial States are not orphans
				if (destination.equals(this.goalState)
						| destination.equals(this.initState)) {
					orphan = false;
					continue;
				}
				Transition t = new Transition(source, destination);
				if (transitions.contains(t)) {
					orphan = false;
				}
			}
			// If current state is orphan we add a transition from the initial
			// state
			if (orphan) {
				createTransition(this.initState, destination);
				log.debug("State " + destination.getName()
						+ " is orphan, fixing it.");
			}
		}
	}

	public String getPrismModel() {
		finalizeModel();
		return PRISMGenerator.getPrismModel(this.initState, this.goalState,
				states, transitions);
	}
	
}
